package com.authine.cloudpivot.ext.controller;


import cn.hutool.core.date.DateUtil;
import com.authine.cloudpivot.engine.api.model.organization.UserModel;
import com.authine.cloudpivot.engine.api.model.security.ClientModel;
import com.authine.cloudpivot.engine.api.model.system.LoginLogModel;
import com.authine.cloudpivot.engine.domain.organization.User;
import com.authine.cloudpivot.engine.enums.type.LoginSourceType;
import com.authine.cloudpivot.engine.service.organization.UserService;
import com.authine.cloudpivot.web.api.controller.base.BaseController;
import com.authine.cloudpivot.web.api.controller.login.HttpClientHelper;
import com.authine.cloudpivot.web.api.controller.login.LoginController;
import com.authine.cloudpivot.web.api.exception.ResultEnum;
import com.authine.cloudpivot.web.api.handler.CustomizedOrigin;
import com.authine.cloudpivot.web.api.service.EngineService;
import com.authine.cloudpivot.web.api.util.ClientUtils;
import com.google.common.collect.Maps;
import eu.bitwalker.useragentutils.Browser;
import eu.bitwalker.useragentutils.UserAgent;
import eu.bitwalker.useragentutils.Version;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.client.CookieStore;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.cookie.Cookie;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLDecoder;
import java.util.*;
import java.util.concurrent.TimeUnit;

@Api(
        tags = {"登录::认证授权"}
)
@RestController
@Validated
@RequestMapping({"/login/Authentication"})
@CustomizedOrigin(
        level = 1
)
public class CustLoginController extends BaseController {
    private static final Logger log = LoggerFactory.getLogger(LoginController.class);
    private static final String REDIS_LOGIN_FAILED_KEY = "login_failed_count";
    public static final String REDIS_LOGIN_AUTH_SUCCESS_USER = "login_auth_success_user:";
    private static final int LOGIN_FAILED_COUNT = 3;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private EngineService engineService;
    @Value("${cloudpivot.login.recordlog.enabled:true}")
    private boolean enabledRecordLoginLog;
    private static final String HTTP_HEADER_USER_AGENT = "User-Agent";
    public static final String DING_SCAN_URL = "/login/dingtalk";
    public static final String DING_MOBILE_LOGIN_AJAX_URL = "/login/mobile/ajax";

    @Autowired
    private UserService userService;

    public CustLoginController() {
    }

    @ApiOperation(
            value = "获取OAuth Code",
            notes = "获取授权码"
    )
    @PostMapping({"/get_code"})
    @ApiImplicitParam(
            name = "params",
            value = "{\"username\":\"用户账号\",\"password\":\"登录密码\",\"url\":获取授权码的请求url,\"corpId\":组织id}",
            required = true,
            dataType = "Map"
    )
    @ResponseBody
    public Map<String, Object> getCode(@RequestBody Map<String, String> params, HttpServletRequest request, HttpServletResponse response) {
        String username = (String)params.get("username");
        String password = (String)params.get("password");
        String url = (String)params.get("url");
        String index = (String)params.get("index");
        String corpId = (String)params.get("corpId");
        Map<String, String> map = new HashMap();
        Integer count = (Integer)this.redisTemplate.opsForValue().get("login_failed_count" + username);
        if (count != null && count >= 3) {
            log.debug("失败{}次，请一分钟后重试", 3);
            Map<String, Object> mapTemplate = new HashMap();
            mapTemplate.put("errcode", 10001);
            mapTemplate.put("errmsg", "失败三次，请一分钟后再试");
            return mapTemplate;
        } else {
            if (StringUtils.isNotEmpty(index)) {
                String privateKey = (String)this.redisTemplate.opsForValue().get(index);
                if (StringUtils.isEmpty(privateKey)) {
                    log.debug("rsa key不存在");
                    Map<String, Object> mapTemplate = new HashMap();
                    mapTemplate.put("errcode", 10001);
                    mapTemplate.put("errmsg", "index缺失，请重新登录");
                    return mapTemplate;
                }
            }

            if (!"admin".equals(username)) {

                //把中文空格转成英文空格,分割用户名取出员工,通过员工号获取用户名
                String[] usernames = username.trim().replaceAll("　"," ").split(" ");
                User user = userService.searchUserByEmployeeNoAndCorpId(usernames[0].trim(), corpId);

                if (Objects.isNull(user) || (usernames.length==2 && !usernames[1].trim().equals(user.getName()))) {
                    log.debug("当前用户不在组织内或者用户不存在{}",username);
                    Map<String, Object> mapTemplate = new HashMap();
                    mapTemplate.put("errcode", 1001);
                    mapTemplate.put("errmsg", "当前用户不在组织内或者用户不存在");
                    return mapTemplate;
                }
                username = user.getUsername();
            }

            map.put("username", username);
            map.put("password", password);
            map.put("index", index);
            map.put("corpId", corpId);
            map.put("portal", "true");
            map.put("User-Agent", request.getHeader("User-Agent"));
            map.put("clientIp", request.getRemoteAddr());

            try {
                url = URLDecoder.decode(url, "utf-8");
                HttpClientContext context = HttpClientContext.create();
                Map<String, Object> result = HttpClientHelper.getAouthationPost(url, map, context);
                this.copyCookies(context, response);
                return result;
            } catch (Exception var14) {
                log.error("get oauth code error.", var14);
                return null;
            }
        }
    }

    private String getHostUrl(HttpServletRequest request) {
        String contextPath = request.getContextPath();
        String url = request.getRequestURL().toString();
        String requestUrl = url.substring(0, url.indexOf(contextPath)) + contextPath;
        return requestUrl;
    }

    @ApiOperation(
            value = "此方法用于刷新token，并返回新的token，要求refresh_token是最新的",
            notes = "刷新Token"
    )
    @GetMapping({"/get_refresh_token"})
    @ApiImplicitParams({@ApiImplicitParam(
            name = "url",
            value = "用于刷新token的url",
            required = true,
            dataType = "String",
            paramType = "path"
    ), @ApiImplicitParam(
            name = "client_secret",
            value = "客户端密码",
            defaultValue = "c31b32364ce19ca8fcd150a417ecce58",
            required = false,
            dataType = "String",
            paramType = "path"
    ), @ApiImplicitParam(
            name = "client_id",
            value = "客户端id",
            defaultValue = "api",
            required = true,
            dataType = "String",
            paramType = "path"
    ), @ApiImplicitParam(
            name = "refresh_token",
            value = "用于刷新token，获取token的时候可以拿到",
            required = true,
            dataType = "String",
            paramType = "path"
    )})
    @ResponseBody
    public Map refreshToken(@RequestParam(required = false,defaultValue = "c31b32364ce19ca8fcd150a417ecce58") String client_secret, @RequestParam(required = false,defaultValue = "api") String client_id, @RequestParam String refresh_token, String url, HttpServletRequest request) {
        HashMap<String,Object> result = null;

        try {
            Map<String, String> map = new HashMap();
            map.put("url", this.getHostUrl(request));
            map.put("client_secret", client_secret);
            map.put("refresh_token", refresh_token);
            map.put("client_id", client_id);
            map.put("grant_type", "refresh_token");
            result = (HashMap<String, Object>) HttpClientHelper.getOauthToken(map);
            if (result != null) {
                result.put("errcode", 0);
                return result;
            } else {
                result = new HashMap();
                result.put("errcode", 9999L);
                result.put("errmsg", "刷新token失败");
                return result;
            }
        } catch (Exception var8) {
            result = new HashMap();
            result.put("errcode", 9999L);
            result.put("errmsg", "刷新token失败");
            return result;
        }
    }

    @ApiOperation("获取Token")
    @GetMapping({"/get_token"})
    @ApiImplicitParams({@ApiImplicitParam(
            name = "url",
            value = "用于刷新token的url",
            defaultValue = "http://ip地址或域名/api",
            required = true,
            dataType = "String",
            paramType = "path"
    ), @ApiImplicitParam(
            name = "code",
            value = "授权码",
            required = true,
            dataType = "String",
            paramType = "path"
    ), @ApiImplicitParam(
            name = "client_secret",
            value = "客户端密码",
            defaultValue = "c31b32364ce19ca8fcd150a417ecce58",
            required = true,
            dataType = "String",
            paramType = "path"
    ), @ApiImplicitParam(
            name = "client_id",
            value = "客户端id",
            defaultValue = "api",
            required = true,
            dataType = "String",
            paramType = "path"
    ), @ApiImplicitParam(
            name = "redirect_uri",
            value = "重定向URI",
            defaultValue = "http://ip地址或域名/oauth",
            required = true,
            dataType = "String",
            paramType = "path"
    )})
    @ResponseBody
    public Map getToken(String url, String code, String client_secret, String client_id, String redirect_uri, HttpServletRequest request) {
        ClientModel clientModel = this.engineService.getSystemSecurityFacade().getClientById(client_id);
        if (clientModel == null) {
            return null;
        } else {
            String registeredRedirectUris = clientModel.getRegisteredRedirectUris();
            if (StringUtils.isNotEmpty(registeredRedirectUris)) {
                try {
                    boolean find = false;
                    URL redirectUrl = new URL(url);
                    String[] strings = registeredRedirectUris.split(",");
                    String[] var12 = strings;
                    int var13 = strings.length;

                    for(int var14 = 0; var14 < var13; ++var14) {
                        String string = var12[var14];
                        URL u = new URL(string);
                        if (redirectUrl.getHost().equals(u.getHost())) {
                            find = true;
                        }
                    }

                    if (!find) {
                        return null;
                    }
                } catch (MalformedURLException var18) {
                    log.error(var18.getMessage(), var18);
                    return null;
                }
            }

            Map<String, String> map = new HashMap();
            map.put("url", url);
            map.put("client_secret", client_secret);
            map.put("redirect_uri", redirect_uri);
            map.put("client_id", client_id);
            map.put("grant_type", "authorization_code");
            map.put("code", code);
            Map result = HttpClientHelper.getOauthToken(map);
            if (result != null) {
                result.put("success", true);

                try {
                    if (result.get("access_token") != null && result.get("refresh_token") != null && result.get("exp") != null) {
                        String exp = String.valueOf(result.get("exp"));
                        exp = exp.length() == 10 ? exp : exp.substring(0, 10);
                        long betweenExpire = DateUtil.betweenMs(new Date(), new Date(Long.parseLong(exp) * 1000L));
                        this.redisTemplate.opsForValue().set("login_auth_success_user:" + result.get("access_token"), result.get("refresh_token") + "," + result.get("exp") + "000", betweenExpire, TimeUnit.MILLISECONDS);
                    }
                } catch (Exception var17) {
                    log.error("解析异常登录信息：", var17);
                }

                if (this.enabledRecordLoginLog) {
                    UserModel userModel = this.engineService.getOrganizationFacade().getUser(String.valueOf(result.get("user_id")));
                    this.createLoginLog(request, userModel);
                }
            }

            return result;
        }
    }

    protected void createLoginLog(HttpServletRequest request, UserModel user) {
        String userAgentHeader = request.getHeader("User-Agent");
        if (userAgentHeader.contains("Apache-HttpClient")) {
            userAgentHeader = request.getParameter("User-Agent");
        }

        UserAgent userAgent = UserAgent.parseUserAgentString(userAgentHeader);
        Browser browser = userAgent.getBrowser();
        if (log.isDebugEnabled()) {
            log.debug("request Header is : {}", userAgentHeader);
            log.debug("Browser is : {} ", userAgent.getBrowser());
            log.debug("version is : {} ", browser.getVersion(userAgentHeader));
        }

        Version version = browser.getVersion(userAgentHeader);
        String browserStr = browser.getName();
        if (null != version) {
            String versionStr = version.getMajorVersion() + "." + version.getMinorVersion();
            browserStr = browserStr + "/" + versionStr;
        }

        LoginLogModel model = new LoginLogModel();
        model.setName(user.getName());
        String url = request.getRequestURI();
        if ("/login/dingtalk".equals(url)) {
            model.setLoginSourceType(LoginSourceType.PORTAL);
        } else if ("/login/mobile/ajax".equals(url)) {
            model.setLoginSourceType(LoginSourceType.DINGTALK);
        } else {
            model.setLoginSourceType(LoginSourceType.PORTAL);
        }

        model.setUserId(user.getSourceId());
        model.setUsername(user.getUsername());
        model.setLoginTime(new Date());
        model.setIpAddress(ClientUtils.getClientIP(request));
        model.setBrowser(browserStr);
        model.setClientAgent(userAgentHeader);
        this.engineService.getSystemManagementFacade().addLoginLog(model);
    }

    @ApiOperation("根据access_token获取refresh_token")
    @PostMapping({"/get_refresh_token"})
    @ApiImplicitParams({@ApiImplicitParam(
            name = "access_token",
            value = "用于获取refresh_token",
            required = true,
            dataType = "String",
            paramType = "path"
    )})
    @ResponseBody
    public Map getRefreshToken(@RequestBody Map<String, String> params) {
        Map<String, Object> map = Maps.newHashMap();
        Map<String, String> data = null;
        Long errcode = ResultEnum.SUCCESS.getErrCode();
        String errmsg = ResultEnum.SUCCESS.getErrMsg();
        String refresh = (String)this.redisTemplate.opsForValue().get("login_auth_success_user:" + (String)params.get("access_token"));
        if (StringUtils.isNotBlank(refresh)) {
            String[] refreshArr = refresh.split(",");
            if (refreshArr.length == 2) {
                data = Maps.newHashMap();
                data.put("refresh_token", refreshArr[0]);
                String expirationStr = refreshArr[1];
                if (StringUtils.isNotEmpty(expirationStr) && expirationStr.length() == 13) {
                    long result = System.currentTimeMillis() / 1000L + 28800L;
                    expirationStr = String.valueOf(result);
                    this.redisTemplate.opsForValue().set("login_auth_success_user:" + (String)params.get("access_token"), refreshArr[0] + "," + expirationStr + "000");
                }

                data.put("expiration", expirationStr);
            } else {
                errcode = ResultEnum.JSON_PASE_ERR.getErrCode();
                errmsg = ResultEnum.JSON_PASE_ERR.getErrMsg();
            }
        } else {
            errcode = ResultEnum.ACCESS_TOKEN_INVALID.getErrCode();
            errmsg = ResultEnum.ACCESS_TOKEN_INVALID.getErrMsg();
        }

        map.put("data", data);
        map.put("errcode", errcode);
        map.put("errmsg", errmsg);
        return map;
    }

    private void copyCookies(HttpClientContext context, HttpServletResponse response) {
        CookieStore cookieStore = context.getCookieStore();
        if (cookieStore != null) {
            List<Cookie> cookies = cookieStore.getCookies();
            cookies.forEach((cookie) -> {
                javax.servlet.http.Cookie c = this.aCookieToSCookie(cookie);
                response.addCookie(c);
            });
        }

    }

    private javax.servlet.http.Cookie aCookieToSCookie(Cookie cookie) {
        javax.servlet.http.Cookie c = new javax.servlet.http.Cookie(cookie.getName(), cookie.getValue());
        c.setComment(cookie.getComment());
        c.setDomain(cookie.getDomain());
        c.setSecure(cookie.isSecure());
        c.setPath(cookie.getPath());
        Date expiryDate = cookie.getExpiryDate();
        if (expiryDate != null) {
            int t = (int)((expiryDate.getTime() - System.currentTimeMillis()) / 1000L);
            c.setMaxAge(t);
        }

        c.setVersion(cookie.getVersion());
        return c;
    }



}
